package service

import (
	"bytes"
	"context"
	"encoding/json"
	"fmt"
	"github.com/elastic/go-elasticsearch/v7/esapi"
	"gorm.io/gorm"
	"log"
	"strconv"
	"time"
	"video-srv/basic/global"
	"video-srv/pkg"

	__ "video-srv/basic/proto"
	"video-srv/internal/model"
)

type Service struct {
	__.UnimplementedVideoServer
}

func (s *Service) VideoAdd(_ context.Context, req *__.VideoAddReq) (resp *__.VideoAddResp, err error) {

	video := model.Video{
		Title:        req.Title,
		Introduce:    req.Introduce,
		CoverUrl:     req.CoverUrl,
		Category:     req.Category,
		Types:        req.Types,
		Actor:        req.Actor,
		Year:         req.Year,
		Address:      req.Address,
		PlayCount:    0,
		LikeCount:    0,
		CollectCount: 0,
		CommentCount: 0,
		Hot:          0,
		Status:       0,
		Score:        0,
	}

	err = global.DB.Debug().Create(&video).Error
	if err != nil {
		return nil, err
	}

	resp = &__.VideoAddResp{Id: int64(video.ID)}

	return resp, err
}

func (s *Service) VideoList(_ context.Context, req *__.VideoListReq) (resp *__.VideoListResp, err error) {

	var videos []model.Video

	tx := global.DB.Debug().Model(&model.Video{}).Scopes(Paginate(req.Page, req.Size))

	if req.Types != "" {
		tx = tx.Where("types = ?", req.Types)
	}

	if req.Address != "" {
		tx = tx.Where("address = ?", req.Address)
	}

	if req.Category != "" {
		tx = tx.Where("category = ?", req.Category)
	}

	tx.Find(&videos)
	
	var videoList []*__.VideoItem
	for _, video := range videos {
		list := &__.VideoItem{
			Title:     video.Title,
			Introduce: video.Introduce,
			CoverUrl:  video.CoverUrl,
			Category:  video.Category,
			Types:     video.Types,
			Actor:     video.Actor,
			Year:      video.Year,
			Address:   video.Address,
			Score:     float32(video.Score),
			PlayCount: video.PlayCount,
		}
		videoList = append(videoList, list)
	}

	resp = &__.VideoListResp{List: videoList}

	marshal, err := json.Marshal(resp)
	if err != nil {
		return nil, err
	}

	err = global.Rdb.Set(global.Ctx, "videoList", marshal, time.Minute*30).Err()
	if err != nil {
		return nil, err
	}

	return resp, err
}

func Paginate(page, size int64) func(db *gorm.DB) *gorm.DB {
	return func(db *gorm.DB) *gorm.DB {

		if page <= 0 {
			page = 1
		}

		size = 20

		offset := (page - 1) * size
		return db.Offset(int(offset)).Limit(int(size))
	}
}

func (s *Service) VideoReview(_ context.Context, req *__.VideoReviewReq) (resp *__.VideoReviewResp, err error) {

	var video model.Video
	global.DB.Debug().Where("id = ?", req.Id).First(&video)

	review, err := pkg.VideoReview(video.Title)
	if err != nil {
		return nil, fmt.Errorf("审核失败")
	}

	if review == false {
		global.DB.Debug().Model(&video).Update("status", 2)
	} else {
		global.DB.Debug().Model(&video).Update("status", 1)
	}

	resp = &__.VideoReviewResp{Message: "审核通过"}
	return resp, nil
}

func (s *Service) VideoDetail(_ context.Context, req *__.VideoDetailReq) (resp *__.VideoDetailResp, err error) {

	var videos []model.Video

	err = global.DB.Debug().Where("id = ?", req.Id).First(&videos).Error

	if err != nil {
		return nil, fmt.Errorf("当前视频不存在")
	}

	var videoList []*__.VideoDetailItem
	for _, video := range videos {
		list := &__.VideoDetailItem{
			Title:     video.Title,
			Introduce: video.Introduce,
			CoverUrl:  video.CoverUrl,
			Category:  video.Category,
			Types:     video.Types,
			Actor:     video.Actor,
			Year:      video.Year,
			Address:   video.Address,
			Score:     float32(video.Score),
			PlayCount: video.PlayCount,
		}
		videoList = append(videoList, list)
	}

	resp = &__.VideoDetailResp{List: videoList}

	return resp, err
}

func (s *Service) EpisodeList(_ context.Context, req *__.EpisodeListReq) (resp *__.EpisodeListResp, err error) {

	var episodes []model.Episode

	err = global.DB.Debug().Where("video_id = ?", req.VideoId).Find(&episodes).Error

	if err != nil {
		return nil, fmt.Errorf("当前视频不存在")
	}

	var videoList []*__.EpisodeItem
	for _, episode := range episodes {
		list := &__.EpisodeItem{
			VideoId:    episode.VideoId,
			Title:      episode.Title,
			EpisodeNum: episode.EpisodeNum,
			Duration:   episode.Duration,
			FileUrl:    episode.FileUrl,
		}
		videoList = append(videoList, list)
	}

	resp = &__.EpisodeListResp{List: videoList}

	return resp, err
}

func (s *Service) PlayLogCreat(_ context.Context, req *__.PlayLogCreatReq) (resp *__.PlayLogCreatResp, err error) {

	log := model.PlayLog{}

	global.DB.Debug().Where("video_id = ?", req.VideoId).First(&log)
	log = model.PlayLog{
		UserId:   req.UserId,
		VideoId:  req.VideoId,
		Duration: req.Duration,
	}

	err = global.DB.Debug().Create(&log).Error
	if err != nil {
		return nil, fmt.Errorf("存储失败")
	}

	resp = &__.PlayLogCreatResp{Msg: "存储成功"}

	return resp, err
}

func (s *Service) PlayLogList(_ context.Context, req *__.PlayLogListReq) (resp *__.PlayLogListResp, err error) {

	var playLogs []model.PlayLog

	err = global.DB.Debug().Where("user_id = ?", req.UserId).Find(&playLogs).Error
	if err != nil {
		return nil, fmt.Errorf("查询失败")
	}

	var videoList []*__.PlayLogItem
	for _, logs := range playLogs {
		list := &__.PlayLogItem{
			UserId:   logs.UserId,
			VideoId:  logs.VideoId,
			Duration: logs.Duration,
		}
		videoList = append(videoList, list)
	}

	resp = &__.PlayLogListResp{List: videoList}

	return resp, err
}

func (s *Service) VideoTop(_ context.Context, req *__.VideoTopReq) (resp *__.VideoTopResp, err error) {

	var videos []model.Video

	global.DB.Debug().Order("hot Desc").Find(&videos)

	var videoList []*__.VideoItem
	for _, video := range videos {
		list := &__.VideoItem{
			Title:     video.Title,
			Introduce: video.Introduce,
			CoverUrl:  video.CoverUrl,
			Category:  video.Category,
			Types:     video.Types,
			Actor:     video.Actor,
			Year:      video.Year,
			Address:   video.Address,
			Score:     float32(video.Score),
			PlayCount: video.PlayCount,
			Hot:       float32(video.Hot),
		}
		videoList = append(videoList, list)
	}

	resp = &__.VideoTopResp{List: videoList}

	marshal, err := json.Marshal(resp)

	err = global.Rdb.Set(global.Ctx, "top", marshal, time.Minute*30).Err()
	if err != nil {
		return nil, err
	}

	return resp, err
}

func (s *Service) VideoEsAdd(_ context.Context, in *__.VideoEsAddReq) (resp *__.VideoEsAddResp, err error) {

	var video model.Video

	// Build the request body.
	data, err := json.Marshal(video)
	if err != nil {
		log.Fatalf("Error marshaling document: %s", err)
	}

	// Set up the request object.
	req := esapi.IndexRequest{
		Index:      "video",
		DocumentID: strconv.Itoa(int(video.ID)),
		Body:       bytes.NewReader(data),
		Refresh:    "true",
	}

	// Perform the request with the client.
	res, err := req.Do(context.Background(), global.Es)
	if err != nil {
		log.Fatalf("Error getting response: %s", err)
	}
	defer res.Body.Close()

	return resp, nil
}
func (s *Service) VideoSearch(_ context.Context, in *__.VideoSearchReq) (resp *__.VideoSearchResp, err error) {
	keyword := in.KeyWord

	var buf bytes.Buffer
	query := map[string]interface{}{
		"query": map[string]interface{}{
			"match": map[string]interface{}{
				"Title": keyword,
			},
		},
	}
	if err := json.NewEncoder(&buf).Encode(query); err != nil {
		log.Fatalf("Error encoding query: %s", err)
	}

	es := global.Es
	// Perform the search request.
	res, err := es.Search(
		es.Search.WithContext(context.Background()),
		es.Search.WithIndex("video"),
		es.Search.WithBody(&buf),
		es.Search.WithTrackTotalHits(true),
		es.Search.WithPretty(),
	)
	if err != nil {
		log.Fatalf("Error getting response: %s", err)
	}
	defer res.Body.Close()
	var r map[string]interface{}
	if err := json.NewDecoder(res.Body).Decode(&r); err != nil {
		log.Fatalf("Error parsing the response body: %s", err)
	}

	fmt.Println(r)
	var list []*__.VideoItem

	for _, hit := range r["hits"].(map[string]interface{})["hits"].([]interface{}) {
		//fmt.Println(hit.(map[string]interface{})["_source"])
		source := hit.(map[string]interface{})["_source"].(map[string]interface{})
		list = append(list, &__.VideoItem{
			Title:    source["Title"].(string),
			CoverUrl: source["CoverUrl"].(string),
		})
	}

	return &__.VideoSearchResp{
		List: list,
	}, nil
}
